/**
 *
 */
package com.storm.lib.template;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.util.List;

import net.sf.ehcache.Cache;
import net.sf.ehcache.CacheManager;
import net.sf.ehcache.Element;

import org.apache.log4j.Logger;
import org.hibernate.HibernateException;
import org.hibernate.Session;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.orm.hibernate3.HibernateTemplate;
import org.springframework.stereotype.Service;

import com.storm.lib.base.BasePo;
import com.storm.lib.exception.InvalidParamException;
import com.storm.lib.exception.InvalidResultException;
import com.storm.lib.exception.NullParamException;
import com.storm.lib.util.DBUtil;
import com.storm.lib.util.ExceptionUtil;
import com.storm.lib.util.FileUtil;


/**
 * 类功能: 基于EhCache的内存操作
 * @author johnny
 * @version 2010-12-28
 */
@Service
public class MemoryTemplate {

	@Autowired
	private HibernateTemplate hibernateTemplate;
	protected final Logger logger = Logger.getLogger(getClass());
	private static Cache userPoCache;
	private static CacheManager manager = null;

	public void clearCache(){
		//TODO 复查-检查完善
		logger.debug("清理Ehcache的缓存");
		List<String> keys =userPoCache.getKeys();
		for (String key : keys) {
			BasePo basePo = (BasePo) userPoCache.get(key).getObjectValue();
			logger.debug("清空淘汰"+basePo.getClass().getSimpleName()+basePo.getId());
			if (DBUtil.isDBSyncClass(basePo)) {
				syncToDB(basePo);
			}
		}
		userPoCache.removeAll();
	}

	public long getObjectInt(){
		return userPoCache.getStatistics().getObjectCount();
	}

	public void init(){
		FileInputStream fis=null;
		try {
			fis = new FileInputStream(FileUtil.getUnderConfigFile("ehcache.xml"));
		} catch (FileNotFoundException e) {
			ExceptionUtil.processException(e);
		}
		manager = new CacheManager(fis);
		userPoCache = manager.getCache("userPoCache");
	}

	public MemoryTemplate() {

	}

	/**
	 *
	 * 更新时间:2010-12-28, 作者:johnny
	 * 方法功能:写一个字符串到Memory，不带参数验证
	 * @param key
	 * @param value
	 * @throws InvalidParamException
	 * @throws NullParamException
	 * @see #writeObjectToMc
	 * @throws NullParamException
	 */
	private void writeStringToMemory(String key,String value) throws InvalidParamException{
		writeObjectToMemory(key,value);
	}

	/**
	 *
	 * 更新时间:2010-12-28, 作者:johnny
	 * 方法功能:从Memory返回字符串,有验证
	 * @param key key
	 * @return 空或者字符串
	 * @throws NullParamException
	 * @throws InvalidResultException
	 */
	private String getStringFromMemory(String key) throws NullParamException, InvalidResultException{
		Object obj = getObjectFromMemory(key);
		if(obj==null){
			return null;
		}
		if((obj instanceof String)==false){
			throw new InvalidResultException();
		}
		return (String)obj;
	}

	/**
	 *
	 * 更新时间:2010-12-28, 作者:johnny
	 * 方法功能:写值到mc,带验证
	 * @param key key值
	 * @param obj 参数值
	 * @throws NullParamException
	 * @throws InvalidParamException
	 */
	public void writeObjectToMemory(String key, Object obj) throws NullParamException, InvalidParamException {
		if(key==null || obj==null){
			throw new NullParamException();
		}
		if("".equals(key)){
			throw new InvalidParamException();
		}
		Element element = new Element(key, obj);
		userPoCache.put(element);
	}

	/**
	 *
	 * 更新时间:2010-12-28, 作者:johnny
	 * 方法功能:返回MC得到的值，带验证
	 * @param key
	 * @return 得到mc的值，可能为null
	 * @throws NullParamException
	 */
	public Object getObjectFromMemory(String key) throws NullParamException{
		if(key==null){
			throw new NullParamException();
		}
		Element element = userPoCache.get(key);
		if(element==null){
			return null;
		}
		if(element.isExpired()){
			return null;
		}
		Object obj = element.getValue();
		return obj;
	}

	/**
	 *
	 * 更新时间:2010-12-28, 作者:johnny
	 * 方法功能:保存或者覆盖一个对象
	 * @param obj
	 */
	public void put(BasePo obj) {
		String key=getObjectKey(obj);
		writeObjectToMemory(key, obj);
		logger.debug("Memory 已修改值 "+key);
	}

	/**
	 *
	 * 更新时间:2010-12-28, 作者:johnny
	 * 方法功能:从数据库更新对象
	 * @param obj
	 */
	public void syncFromDB(BasePo obj) {
		String key=getObjectKey(obj);
		writeObjectToMemory(key, obj);
		logger.debug("Memory 已从数据库同步 "+key);
	}

	/**
	 *
	 * 更新时间:2010-12-28, 作者:johnny
	 * 方法功能:从数据库更新对象
	 * @param obj
	 */
	public void removeKey(BasePo obj) {
		String key=getObjectKey(obj);
		userPoCache.remove(key);
	}


//	/**
//	 *
//	 * 更新时间:2010-12-28, 作者:johnny
//	 * 方法功能:保存或者覆盖一个对象,加载时使用
//	 * @param obj
//	 */
//	public void load(BasePo obj) {
//		String key=getObjectKey(obj);
//		writeObjectToMemory(key, obj);
//		logger.debug("Memory 已加载 "+key);
//	}
	/**
	 *
	 * 更新时间:2010-12-28, 作者:johnny
	 * 方法功能: 获得对象的key
	 * @param obj
	 */
	private String getObjectKey(BasePo obj) {
		String className=obj.getClass().getName();
		Integer id = obj.getId();
		if(id==null){
			ExceptionUtil.throwInvalidParamException();
		}
		String objectKey = className+":"+id;
		return objectKey;
	}

	/**
	 *
	 * 更新时间:2010-12-28, 作者:johnny
	 * 方法功能: 获得对象的key
	 * @param obj
	 */
	private String getObjectKey(Integer id, Class class1) {
		String className=class1.getName();
		String objectKey = className+":"+id;
		return objectKey;
	}

	/**
	 *
	 * 更新时间:2010-12-28, 作者:johnny
	 * 方法功能:memory读取对象
	 * @param id
	 * @param class1
	 * @return
	 */
	public BasePo get(Integer id, Class class1) {
		String key=getObjectKey(id,class1);
		Object obj = getObjectFromMemory(key);
		if(obj==null){
			logger.debug("Memory 未命中 "+key);
			return null;
		}
		else{
			logger.debug("Memory 已命中 "+key);
			return (BasePo) getObjectFromMemory(key);
		}
	}

	/**
	 *
	 * 更新时间:2010-12-28, 作者:johnny
	 * 方法功能:清空Memory
	 */
	public void clear() {
		userPoCache.removeAll();
	}

	/**
	 * 方法功能:判断是否内存已经有这个对象
	 * 更新时间:2010-12-29, 作者:johnny
	 * @param clazz
	 * @param id
	 * @return
	 */
	public boolean has(Class<BasePo> clazz, int id) {
		String key=getObjectKey(id,clazz);
		if(userPoCache.isKeyInCache(key)){
			return true;
		}
		return false;
	}

//	/**
//	 * 方法功能:返回加载的对象
//	 * 更新时间:2010-12-29, 作者:johnny
//	 * @param clazz
//	 * @param id
//	 * @return
//	 */
//	public BasePo getLoaded(Class clazz, int id) {
//		String key=getObjectKey(id,clazz);
//		return (BasePo) memory.get(key);
//	}



//	/**
//	 * 方法功能:关联到内存
//	 * 更新时间:2010-12-29, 作者:johnny
//	 * @param basePo
//	 */
//	public void setToMemory(BasePo basePo) {
//
//	}

	/**
	 * 方法功能:初期加载用户数据到内存
	 * 更新时间:2010-12-29, 作者:johnny
	 */
	public void loadUserDataToMemory() {

	}

	/**
	 * 方法功能:内存数据写到数据库
	 * 更新时间:2010-12-29, 作者:johnny
	 */
	public void flushDatasToDB() {
		//TODO 复查-是否使用不同的Session
		logger.debug("刷新内存到数据库");
		int proceedNumber= 0;
		final int flushCount = 30;
		Session session = hibernateTemplate.getSessionFactory().openSession();
		int currentCount = 0;
		List<String> keys =userPoCache.getKeysWithExpiryCheck();
		for (String key : keys) {
			BasePo po = (BasePo) userPoCache.get(key).getValue();
			if(updatePo2DB(session, flushCount, currentCount++,po)){
				proceedNumber++;
			}
		}
		session.flush();
		session.clear();
		session.close();
		logger.info("本次操作同步了对象:"+proceedNumber);
//		logger.fatal("["+new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())+"] Memory进行了一次同步数据库...");
	}

	/**
	 *
	 * 方法功能: 写同一张表数据
	 * 更新时间:2011-2-9, 作者:winger
	 * @param sessions
	 * @param flushCount
	 * @param currentCount
	 * @param po
	 * @param index
	 */
	private boolean updatePo2DB(final Session session, final int flushCount,final int currentCount, final BasePo po) {
		//TODO 复查-可能的隐患,可能新到内存的对象还在初始化时就因为池满被踢出去
		if (DBUtil.isDBSyncClass(po)) {
			if(po.gLoaded()==false){
				//ExceptionUtil.throwInvalidParamException("Loaded =false,skip"+po.fetchIdentify());
				return false;
			}
			po.saveData();
			if(po.gWasChanged()==false){
				logger.debug("[更新数据库]对象没变化,跳过同步"+po.fetchIdentify());
				return false;
			}
			session.update(po);
			po.unChanged();
			logger.debug("[更新数据库]同步"+po.fetchIdentify());
			if (currentCount%flushCount==0) {
				try {
					session.flush();
					session.clear();
//					logger.debug("清理Session");
				} catch (HibernateException e) {
					logger.error(e.getMessage());
				}
			}
			return true;
		}
		return false;
	}

	/**
	 * 方法功能:同步到数据库
	 * 更新时间:2011-2-22, 作者:johnny
	 * @param basePo
	 */
	public void syncToDB(BasePo basePo) {
		Session session = hibernateTemplate.getSessionFactory().openSession();
		updatePo2DB(session, 30, 1,basePo);
		session.flush();
		session.clear();
		logger.debug("更新"+basePo.getId()+"到数据库成功");
		session.close();
	}

	public void syncToDB(List<BasePo> list) {
		Session session = hibernateTemplate.getSessionFactory().openSession();
		int i=0;
		for (BasePo basePo2 : list) {
			updatePo2DB(session, 30, i++,basePo2);
			logger.debug("更新"+basePo2+"到数据库成功");
		}
		session.flush();
		session.clear();
		session.close();
	}




}
